根据底层 GPU 架构,对于每个绘制调用,GPU 可以并行执行很多阶段的渲染工作,例如顶点处理、片段处理和内存读取。绘制调用会等待,直到所有片段都得到处理为止。如果片段着色器的执行速度比顶点着色器或其他阶段慢,则其他阶段需要等待片段着色器执行完毕。
您可以通过以下方法优化片段着色器:
如果性能瓶颈在于片段着色,精度从两个周期降至一个周期会提升 GPU 一半的性能。
要降低像素着色器的精度:
precision
,使用适当的值范围:lowp
适用于颜色范围(RGB 数据范围 [0..1])和强度范围 [0..1] 等数据,但不适用于需要更高精度的纹理坐标等数据。lowp
支持范围是 [-2..2],小数点后保留 8 位精度。mediump
适用于大多数渲染,矩阵需要具有更高的精度,原因在于浮点值相对较小。highp
包含精确的 3D 渲染表示,包括矩阵。uniform sampler2D Texture; uniform lowp float BlendIntensity; varying mediump vec2 vTexCoord; void main() { precision lowp float; vec4 color = texture2D(Texture, vTexCoord); gl_FragColor.rgba = color.rgba * BlendIntensity; }
uniform sampler2D Texture; uniform mediump float BlendIntensity; //与 lowp 相比,mediump 将周期次数增加了一倍 varying mediump vec2 vTexCoord; void main() { precision mediump float; //与 lowp 相比,mediump 将周期次数增加了一倍 vec4 color = texture2D(Texture, vTexCoord); gl_FragColor.rgba = color.rgba * BlendIntensity; }
使用顶点着色器计算需要保持不变并只需要计算几次的值。对光照计算执行类似操作可以将一个顶点的结果插值到另一个顶点中而且不会导致质量明显下降,因为顶点覆盖范围通常比片段覆盖范围小很多(除了高密度的几何结构以外)。
例如,对于顶点着色器,请使用代码:
attribute vec3 kzPosition; attribute vec2 kzTextureCoordinate0; uniform highp mat4 kzProjectionCameraWorldMatrix; uniform mediump float kzTime; varying mediump vec2 vTexCoord; varying lowp vec4 vColor; void main() { precision mediump float; //只对每个顶点执行三角函数运算,例如, //对于 quad 3 * 2 times (2 个三角形,每个三角形各有 3 个顶点) vColor = vec4(sin(kzTime)); gl_Position = kzProjectionCameraWorldMatrix * vec4(kzPosition.xyz, 1.0); }
例如,对于片段着色器,请使用代码:
varying lowp vec4 vColor; void main() { precision lowp float; //对于写入的每个片段,应用具有相同精度 (lowp -> lowp) 的 //常数插值分配。在大多数 GPU 上,分配不应超过 //一个周期。 gl_FragColor.rgba = vColor; }
例如,对于顶点着色器,请勿使用代码:
attribute vec3 kzPosition; uniform highp mat4 kzProjectionCameraWorldMatrix; void main() { precision mediump float; //顶点着色器会输出位置并在片段着色器中执行计算, //当片段数量超过顶点数量时,这并不是一种明智的做法。 gl_Position = kzProjectionCameraWorldMatrix * vec4(kzPosition.xyz, 1.0); }
例如,对于片段着色器,请勿使用代码:
uniform mediump float kzTime; void main() { precision lowp float; //对于写入的每个片段,额外的三角函数 sin() //会被执行。三角函数会消耗大量的资源 - 具体取决于 GPU, //每个片段需要使用多个 GPU 周期。实际上,这样做的结果与 //将结果存储为 varying 是一样的。 gl_FragColor.rgba = vec4(sin(kzTime)); }